#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<unordered_map>
#include<fstream>
#include<pthread.h>
#include"util.hpp"

using std::string;
using std::vector;
using std::unordered_map;
using std::cout;
using std::endl;
using std::cerr;

namespace ns_index
{
    //一个文档有哪些属性字段
    struct DocInfo
    {
        string title;
        string content;
        string url;
        uint64_t doc_id;
    };
    //对倒排索引对象的描述
    struct Inverted_Object
    {
        uint64_t doc_id;
        string key_word;
        int weight;
    };
    typedef vector<Inverted_Object> InvertedList;//倒排拉链
    class Index
    {
    public:
        //根据文档id，找到该文档对象
        DocInfo* GetForwardIndex(uint64_t doc_id)
        {
            if(doc_id>=forward_index.size())
            {
                cerr<<"doc_id is illegal,error！"<<endl;
                return nullptr;
            }
            return &forward_index[doc_id];
        }
        //根据key_word，获得倒排拉链
        InvertedList* GetInvertedIndex(const string& key_word)
        {
            auto iterator_pos=inverted_index.find(key_word);
            if(iterator_pos==inverted_index.end())
            {
                cout<<key_word<<" has no InvertedList"<<endl;
                return nullptr;
            }
            return &(iterator_pos->second);
        }
        //根据文档内容构建索引，src_path就是处理完后的文档路径
        bool BuildIndex(const string& src_path)
        {
            std::ifstream in(src_path,std::ios::in | std::ios::binary);//以二进制方式从src路径下的文件中读取数据
            if(in.is_open()==0)
            {
                cout<<"sorry,"<<src_path<<" open fail..."<<endl;
                return false;
            }
            //文件打开成功
            string line;
            int cnt=0;
            while(getline(in,line))//得到一个文档的title、content、url，将其存放在line中
            {
                DocInfo* doc=BuildForwardIndex(line);//建立正排索引，将一个个文档以doc的形式存储在forward_index中，并以doc_id以下标
                if(doc==nullptr)
                {
                    cerr<<"build forwardindex fail..."<<endl;
                    continue;
                }
                BuildInvertedIndex(*doc);//建立倒排索引，

                if(cnt%50==0)
                    cout<<"已经将"<<cnt++<<"个文档成功建立索引..."<<endl;
                else cnt++;
            }
            return true;
        }

        ~Index()
        {

        }
    private:
        //建立正排索引
        DocInfo* BuildForwardIndex(const string& line)
        {
            //1.解析line，字符串切割
            vector<string> results;
            ns_util::StringUtil::CutString(line,results,"\3");

            //2.填充Doc对象
            DocInfo doc;
            doc.title=results[0],doc.content=results[1],doc.url=results[2],doc.doc_id=forward_index.size();

            //3.将doc添加到forward_index中
            forward_index.push_back(std::move(doc));

            return &forward_index.back();
        }
        //建立倒排索引
        bool BuildInvertedIndex(const DocInfo& doc)
        {
            struct word_cnt
            {
                int title_cnt;
                int content_cnt;
                word_cnt():title_cnt(0),content_cnt(0){}
            };
            std::unordered_map<string,word_cnt> word_map;//用来暂存所有分词的标题频次和内容频次

            //对标题进行分词
            vector<string> title_words;//存放title的所有分词
            ns_util::JiebaUtil::Split(doc.title,title_words);

            //对标题进行词频统计
            for(auto& str:title_words)
            {
                boost::to_lower(str);
                word_map[str].title_cnt++;
            }

            //对内容进行分词
            vector<string> content_words;//存放content的所有分词
            ns_util::JiebaUtil::Split(doc.content,content_words);

            //对内容进行词频统计
            for(auto& str:content_words)
            {
                boost::to_lower(str);
                word_map[str].content_cnt++;
            }

#define X 10
#define Y 1
            //遍历word_map,构建倒排拉链
            for(auto& word_pair:word_map)//word_map是一个文档的所有key及其对应的词频
            {
                Inverted_Object obj;
                obj.doc_id=doc.doc_id;
                obj.key_word=word_pair.first;
                obj.weight=X * word_pair.second.title_cnt + Y * word_pair.second.content_cnt;//相关性
                //将所有的key及其所在文档的信息插入“倒排索引中”
                inverted_index[word_pair.first].push_back(std::move(obj));//移动构造
            }
        }

    private:
        //找到含有某一key_word的所有文档，并按weight排序
        unordered_map<string,InvertedList> inverted_index;//倒排索引，key_word——>文档
        //以特定的数据结构管理众多DocInfo结构体对象
        vector<DocInfo> forward_index;//正排索引，文档id——>文档对象

    private:
        Index(){}
        Index(const Index&)=delete;
        Index& operator=(const Index&)=delete;

        static Index* _instance;
        static pthread_mutex_t* _mtx;

    public:
        static Index* GetInstance()
        {
            if(nullptr==_instance)
            {
                //pthread_mutex_lock(_mtx);
                _instance=new Index();
                //pthread_mutex_unlock(_mtx);
            }
            return _instance;
        }
    };
    Index* Index::_instance=nullptr;
    pthread_mutex_t* Index::_mtx=nullptr;
}